using System;
using System.Timers;
using Kaleida.ServiceMonitor.Framework;

namespace Kaleida.ServiceMonitor.Model
{
    internal class MinuteTimer
    {
        private bool busy;
        private DateTime lastSentWholeMinutes;
        private readonly Timer timeCheckTimer = new Timer(1000) { AutoReset = true };

        public event EventHandler<MinuteTimerTickEventArgs> Tick;

        public MinuteTimer()
        {
            timeCheckTimer.Elapsed += CheckTime;
        }

        public void Start()
        {
            lastSentWholeMinutes = ToDateTimeWithMinutePrecision(DateTime.Now);
            timeCheckTimer.Start();
        }

        private void CheckTime(object sender, ElapsedEventArgs eventArgs)
        {
            if(busy) // i.e. if we're still processing events from the previous minute
                return;

            busy = true;

            // The Tick event should fire once, at the earliest opportunity, for each new minute.
            // If, for whatever reason, we don't get notified until after the minute has passed,
            // the missed events still need to be raised

            var nowInWholeMins = ToDateTimeWithMinutePrecision(DateTime.Now);

            var wholeMinutesSinceLastSent = Math.Truncate((nowInWholeMins - lastSentWholeMinutes).TotalMinutes);
            
            if(wholeMinutesSinceLastSent > 0)
            {
                for (var time = lastSentWholeMinutes.AddMinutes(1); time <= nowInWholeMins; time = time + TimeSpan.FromMinutes(1))
                {
                    var scheduledTime = new ScheduledTime(time.TimeOfDay);
                    
                    if (Tick != null)
                        Tick(this, new MinuteTimerTickEventArgs(scheduledTime));
                }

                lastSentWholeMinutes = nowInWholeMins;
            }

            busy = false;
        }

        private static DateTime ToDateTimeWithMinutePrecision(DateTime now)
        {
            return new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 0);
        }
    }
}